﻿using System;
using System.Collections.Generic;
using System.Linq;
using DDDSample.Domain.Location;
using DDDSample.Domain.PathFinder;
using NHibernate;
using NHibernate.Linq;

namespace DDDSample.Application.PathFinder
{
    public class GraphTraversalService : IGraphTraversalService
    {
        private readonly ISession _session;
        private readonly Random _random = new Random();

        public GraphTraversalService(ISession session)
        {
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            _session = session;
        }

        public IList<TransitPath> FindShortestPaths(string origin, string destination, Constraints limitations)
        {
            var currentDate = NextDate(DateTime.Now);

            IList<String> allVertices = _session.Query<Location>().ToList().Select(l => l.UnLocode.CodeString).ToList();
            allVertices.Remove(origin);
            allVertices.Remove(destination);

            var candidateCount = GetRandomNumberOfCandidates();
            var candidates = new List<TransitPath>();

            for (var i = 0; i < candidateCount; i++)
            {
                allVertices = GetRandomChunkOfLocations(allVertices);
                var transitEdges = new List<TransitEdge>();
                var firstLegTo = allVertices.First();
                var fromDate = NextDate(currentDate);
                var toDate = NextDate(fromDate);
                currentDate = NextDate(toDate);
                transitEdges.Add(new TransitEdge(origin, firstLegTo, fromDate, toDate));

                for (var j = 0; j < allVertices.Count - 1; j++)
                {
                    var curr = allVertices[j];
                    var next = allVertices[j + 1];
                    transitEdges.Add(GetNextEdge(ref currentDate, curr, next));
                }
                var lastLegFrom = allVertices.Last();
                transitEdges.Add(GetNextEdge(ref currentDate, lastLegFrom, destination));

                candidates.Add(new TransitPath(transitEdges));
            }
            return candidates;
        }

        private int GetRandomNumberOfCandidates()
        {
            return 3 + _random.Next(3);
        }

        private DateTime NextDate(DateTime currentDate)
        {
            return currentDate.AddDays(1).AddMinutes(_random.Next(1000) - 500);
        }

        private IList<String> GetRandomChunkOfLocations(IList<string> allLocations)
        {
            var chunk = allLocations.Count > 4 ? 1 + _random.Next(5) : allLocations.Count;
            return allLocations.Select(x => new { Value = x, Index = _random.Next(100) })
               .OrderBy(x => x.Index)
               .Select(x => x.Value)
               .Take(chunk)
               .ToList();
        }

        private TransitEdge GetNextEdge(ref DateTime currentDate, string curr, string next)
        {
            var fromDate = NextDate(currentDate);
            var toDate = NextDate(fromDate);
            currentDate = NextDate(toDate);
            return new TransitEdge(curr, next, fromDate, toDate);
        }
    }
}